﻿#include "FITKGraphObjectRefNode.h"

// VTK
#include <vtkPoints.h>
#include <vtkVertex.h>
#include <vtkCellArray.h>
#include <vtkPolyData.h>
#include <vtkPointData.h>
#include <vtkStringArray.h>
#include <vtkCellData.h>
#include <vtkProperty2D.h>
#include <vtkProperty.h>
#include <vtkLabelHierarchy.h>
#include <vtkPointSetToLabelHierarchy.h>
#include <vtkTextProperty.h>

// Graph
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor2D.h"
#include "FITKGraphCommons.h"

// Mapper and strategy
#include <vtkLabelPlacementMapper.h>
#include <vtkFreeTypeLabelRenderStrategy.h>
#include "FITK_Interface/FITKVTKAlgorithm/FITKPolyPlacementMapper.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKPolyRenderStrategy.h"

// Data
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaRefPoint.h"

namespace Exchange
{
    FITKGraphObjectRefNode::FITKGraphObjectRefNode(AbaqusData::FITKDataCase* caseData, Interface::FITKAbaRefPoint* refNode)
        : FITKGraphObject3D(caseData, refNode)
    {
        if (!refNode)
        {
            return;
        }

        // Initialize.
        init();

        // Get ref-node information.
        double position[3] = { 0, 0, 0 };
        refNode->getCoor(position);
        QString refName = refNode->getDataObjectName();

        // Generate actor.
        generateGraph(refName, position);

        // Set the layer need to be rendered.
        setRenderLayer(1);

        // Save the graph information.
        m_graphInfo.Type = RefNodeGraph;
        m_graphInfo.RefNodeId = refNode->getDataObjectID();
    }

    FITKGraphObjectRefNode::~FITKGraphObjectRefNode()
    {
        // Delete poly data.
        //@{
        deleteVtkObj(m_points);
        deleteVtkObj(m_cells);
        deleteVtkObj(m_polyData);
        //@}

        // Delete data for color.
        //@{
        deleteVtkObj(m_textProperty);
        deleteVtkObj(m_strategyShape);
        //@}
    }

    void FITKGraphObjectRefNode::init()
    {
        // Create poly data.
        //@{
        m_polyData = vtkPolyData::New();
        m_points = vtkPoints::New();
        m_cells = vtkCellArray::New();
        m_textArray = vtkStringArray::New();

        m_textArray->SetNumberOfComponents(1);
        m_textArray->SetName("_name_label_");
        //@}

        // Create text property.
        //@{
        m_textProperty = vtkTextProperty::New();
        m_textProperty->SetBold(false);
        m_textProperty->SetFontSize(m_fontSize);

        // Get the ref-node color.
        double color3[3] = { 0, 0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_refNodeColor, color3);
        m_textProperty->SetColor(color3);
        m_textProperty->SetFontFamilyToArial();
        //@}
    }

    void FITKGraphObjectRefNode::preHighlight()
    {
        // Get the ref-node pre-highlight color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_preHighlightColor, color3);

        // Set the pre-highlight color.
        if (m_strategyShape)
        {
            m_strategyShape->SetColor(color3);
        }

        if (m_textProperty)
        {
            m_textProperty->SetColor(color3);
        }

        this->Superclass::preHighlight();
    }

    void FITKGraphObjectRefNode::highlight(QColor color)
    {
        // If the object is invisible, then return.
        if (!getDataVisibility())
        {
            return;
        }

        // Get the ref-node highlight color.
        double color3[3] = { 0 ,0, 0 };
        if (color.isValid())
        {
            FITKGraphCommons::QColorToDouble3(color, color3);
        }
        else
        {
            FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_refNodeHighlightColor, color3);
        }      

        // Set the highlight color.
        if (m_strategyShape)
        {
            m_strategyShape->SetColor(color3);
        }

        if (m_textProperty)
        {
            m_textProperty->SetColor(color3);
        }

        this->Superclass::highlight();
    }

    void FITKGraphObjectRefNode::disHighlight()
    {
        // Get the ref-node default color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_refNodeColor, color3);

        // Set the default color.
        if (m_strategyShape)
        {
            m_strategyShape->SetColor(color3);
        }

        if (m_textProperty)
        {
            m_textProperty->SetColor(color3);
        }

        this->Superclass::disHighlight();
    }

    void FITKGraphObjectRefNode::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        // Update the visibility first of all.
        updateVisibility();

        Interface::FITKAbaRefPoint* refNode = dynamic_cast<Interface::FITKAbaRefPoint*>(_dataObj);
        if (!refNode)
        {
            return;
        }

        resetVtkObj(m_points);
        resetVtkObj(m_cells);
        resetVtkObj(m_textArray);

        // Get ref-node information.
        double position[3] = { 0, 0, 0 };
        refNode->getCoor(position);
        QString refName = refNode->getDataObjectName();

        // Initializer polydata.
        m_points->InsertNextPoint(position);
        m_cells->InsertNextCell(1);
        m_cells->InsertCellPoint(0);
        m_textArray->InsertNextValue(refName.toUtf8());
    }

    bool FITKGraphObjectRefNode::getFixedBounds(double* bounds)
    {
        // Get the point's bounds only.
        if (!m_fActorPoint3D)
        {
            return false;
        }

        double bds[6] = { 0., 0., 0., 0., 0., 0. };
        bool flag = getActorBounds(m_fActorPoint3D, bds);
        if (!flag)
        {
            return false;
        }

        for (int i = 0 ; i < 6 ; i++)
        {
            bounds[i] = bds[i];
        }

        return true;
    }

    void FITKGraphObjectRefNode::setPickable(bool isPickable, int dim)
    {
        Q_UNUSED(dim);

        // Set pickable for 2D and 3D actors.
        if (m_fActorPoint3D)
        {
            m_fActorPoint3D->SetPickable(isPickable);
        }

        if (m_fActorText2D)
        {
            m_fActorText2D->SetPickable(isPickable);
        }

        if (m_fActorShape2D)
        {
            m_fActorShape2D->SetPickable(isPickable);
        }
    }

#ifdef QT_DEBUG
    void FITKGraphObjectRefNode::simplified(bool enabled)
    {
        if (!m_graphSimplified)
        {
            return;
        }

        bool dataVisible = getDataVisibility();

        // Change the actor visibility while changing the camera.
        if (dataVisible)
        {
            if (m_fActorText2D)
            {
                m_fActorText2D->SetVisibility(!enabled);
            }

            if (m_fActorShape2D)
            {
                m_fActorShape2D->SetVisibility(!enabled);
            }

            if (m_fActorPoint3D)
            {
                m_fActorPoint3D->SetVisibility(!enabled);
            }
        }
    }
#endif // QT_DEBUG

    void FITKGraphObjectRefNode::generateGraph(QString name, double* pos, double fontSize, double shapeSize)
    {
        if (!m_polyData || !m_points || !m_cells || !m_textArray || !pos)
        {
            return;
        }

        // Add space to the ref-node name.
        name = "  " + name;

        // Get the ref-node color.
        double color3[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(FITKGraphCommons::s_refNodeColor, color3);

        // Initializer polydata.
        m_points->InsertNextPoint(pos);
        m_cells->InsertNextCell(1);
        m_cells->InsertCellPoint(0);
        m_textArray->InsertNextValue(name.toUtf8());

        // Created the polydata needed by labels.
        m_polyData->SetPoints(m_points);
        m_polyData->SetVerts(m_cells);
        m_polyData->GetPointData()->AddArray(m_textArray);

        // The point actor for picking.
        m_fActorPoint3D = FITKGraphActor::New();
        m_fActorPoint3D->setDataType(DataType::GeometryData);
        m_fActorPoint3D->setGraphObject(this);
        m_fActorPoint3D->SetPickable(true);
        m_fActorPoint3D->setInputDataObject(m_polyData);

        // Create 'X' shape actor.
        m_fActorShape2D = FITKGraphActor2D::New();
        m_fActorShape2D->setGraphObject(this);
        m_fActorShape2D->SetPickable(true);
        m_fActorShape2D->setActorType(ActorType2D::GlyphActor);

        // Create text actor.
        m_fActorText2D = FITKGraphActor2D::New();
        m_fActorText2D->setGraphObject(this);
        m_fActorText2D->SetPickable(true);
        m_fActorText2D->setActorType(ActorType2D::TextActor);

        m_textProperty->SetFontSize(fontSize > 0 ? fontSize : m_fontSize);

        // Label hierarchy.
        //@{
        vtkSmartPointer<vtkPointSetToLabelHierarchy> textData = vtkSmartPointer<vtkPointSetToLabelHierarchy>::New();
        textData->SetInputData(m_polyData);
        textData->SetTargetLabelCount(1);
        textData->SetMaximumDepth(15);
        textData->SetLabelArrayName(m_textArray->GetName());
        textData->SetTextProperty(m_textProperty);
        textData->Update();
        //@}

        // Shape mapper and strategy.
        //@{
        vtkSmartPointer<FITKPolyPlacementMapper> mapperShape = vtkSmartPointer<FITKPolyPlacementMapper>::New();
        mapperShape->UseDepthBufferOff();

        m_strategyShape = FITKPolyRenderStrategy::New();
        m_strategyShape->SetSourceType(SourceType::X_Shape);
        m_strategyShape->SetColor(color3);
        m_strategyShape->SetPixelSize(shapeSize > 0 ? shapeSize : m_glyphSize);

        // Set input connecton of label hierarchy and strategy.
        mapperShape->SetInputConnection(textData->GetOutputPort());
        mapperShape->SetRenderStrategy(m_strategyShape);
        mapperShape->SetIteratorType(vtkLabelHierarchy::FULL_SORT);

        // Show all labels
        mapperShape->SetPlaceAllLabels(true);
        mapperShape->SetMaximumLabelFraction(1.);

        mapperShape->Update();

        m_fActorShape2D->SetMapper(mapperShape);
        //@}

        // Text mapper and strategy.
        //@{
        vtkSmartPointer<vtkLabelPlacementMapper> mapperText = vtkSmartPointer<vtkLabelPlacementMapper>::New();
        mapperText->UseDepthBufferOff();
        mapperText->SetShapeToNone();
        mapperText->SetStyleToOutline();
        mapperText->SetOutputTraversedBounds(true);
        mapperText->UseUnicodeStringsOff();

        vtkSmartPointer<vtkFreeTypeLabelRenderStrategy> strategyText = vtkSmartPointer<vtkFreeTypeLabelRenderStrategy>::New();

        // Set input connecton of label hierarchy and strategy.
        mapperText->SetInputConnection(textData->GetOutputPort());
        mapperText->SetRenderStrategy(strategyText);

        // Show all labels
        mapperText->SetPlaceAllLabels(true);
        mapperText->SetMaximumLabelFraction(1.);

        mapperText->SetIteratorType(vtkLabelHierarchy::FULL_SORT);
        mapperText->Update();

        m_fActorText2D->SetMapper(mapperText);
        //@}

        // Add actors.
        addActor(m_fActorShape2D);
        addActor(m_fActorText2D);
        addActor(m_fActorPoint3D);
    }
}   // namespace Exchange